/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.pdx.cs.guavadata.query;

import edu.pdx.cs.guavadata.Channel;
import edu.pdx.cs.guavadata.Transform;
import edu.pdx.cs.guavadata.condition.Condition;
import edu.pdx.cs.guavadata.domain.Domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 *
 * @author VuTran
 */
public class Join extends Operator {

    // Relational algebra join operator: child1 |X| child2
    // This operator has outer join variants

    Operator child1;
    Operator child2;
    List<String> leftColumns;
    List<Condition> rightConditions;
    JoinType type;

    public Operator getChild1() {
        return child1;
    }

    public void setChild1(Operator child1) {
        this.child1 = child1;
    }

    public Operator getChild2() {
        return child2;
    }

    public void setChild2(Operator child2) {
        this.child2 = child2;
    }

    public List<String> getLeftColumns() {
        return leftColumns;
    }

    public void setLeftColumns(List<String> leftColumns) {
        this.leftColumns = leftColumns;
    }

    public List<Condition> getRightConditions() {
        return rightConditions;
    }

    public void setRightConditions(List<Condition> rightConditions) {
        this.rightConditions = rightConditions;
    }

    public JoinType getType() {
        return type;
    }

    public void setType(JoinType type) {
        this.type = type;
    }

    public Join(Operator child1, Operator child2, JoinType jt) {
        this.child1 = child1;
        this.child2 = child2;
        child1.parent = this;
        child2.parent = this;
        this.leftColumns = new ArrayList<String>();
        this.rightConditions = new ArrayList<Condition>();
        this.type = jt;

        // List of columns and domains is equal to the combination of the two from the base expressions
        this.exposedColumns = new ArrayList<String>(this.child1.exposedColumns);
        this.exposedDomains = new ArrayList<Domain>(this.child1.exposedDomains);
        for (int i = 0; i < this.child2.exposedColumns.size(); i++) {
            // Ignore duplicate columns
            if (this.exposedColumns.contains(this.child2.exposedColumns.get(i)))
                continue;

            this.exposedColumns.add(this.child2.exposedColumns.get(i));
            this.exposedDomains.add(this.child2.exposedDomains.get(i));
        }
    }

    public Join(Operator child1, Operator child2, Collection<String> leftColumns,
            Collection<Condition> rightConditions, JoinType jt) {
        this.child1 = child1;
        this.child2 = child2;
        child1.parent = this;
        child2.parent = this;
        this.leftColumns = new ArrayList<String>(leftColumns);
        this.rightConditions = new ArrayList<Condition>(rightConditions);
        this.type = jt;

        // List of columns and domains is equal to the combination of the two from the base expressions
        this.exposedColumns = new ArrayList<String>(this.child1.exposedColumns);
        this.exposedDomains = new ArrayList<Domain>(this.child1.exposedDomains);
        for (int i = 0; i < this.child2.exposedColumns.size(); i++) {
            // Ignore duplicate columns
            if (this.exposedColumns.contains(this.child2.exposedColumns.get(i)))
                continue;

            this.exposedColumns.add(this.child2.exposedColumns.get(i));
            this.exposedDomains.add(this.child2.exposedDomains.get(i));
        }
    }

    @Override
    public SqlText visit(Provider qv) {
        return qv.transform(this);
    }

    @Override
    public void visit(Channel c, String table) {
        c.mapAliases(this, table);
    }

    @Override
    public void visit(Transform t, Command c) {
        t.expand(this, c);
    }

    @Override
    public void notifyChange(Operator old, Operator newOp) {
        if (this.child1 == old) {
            this.child1 = newOp;
            newOp.parent = this;
        }
        if (this.child2 == old) {
            this.child2 = newOp;
            newOp.parent = this;
        }
    }

    public void addCondition(String leftColumn, Condition rightCondition) {
        this.leftColumns.add(leftColumn);
        this.rightConditions.add(rightCondition);
    }

    public String getJoinCondition() {
        try {
            if (this.leftColumns.size() <= 0)
                throw new Exception("Cannot create a join condition with no conditions.");
        } catch (Exception e) {
            System.err.println("ERROR: " + e.toString());
        }

        // Build the join condition
        String build = this.leftColumns.get(0) + this.rightConditions.get(0).toString();
        for (int i = 1; i < this.leftColumns.size(); i++)
            build = build + " AND " + this.leftColumns.get(i) + this.rightConditions.get(i).toString();
        return build;
    }

    @Override
    public Operator clone() {
        return new Join(child1.clone(), child2.clone(), leftColumns, rightConditions, type);
    }


}
